using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace eTMS.Controls
{
    public class TreeView : System.Windows.Forms.TreeView
    {

        #region Selected Node(s) Properties
        private bool _MultiSelection = true;
        Point OldPt = new Point(0, 0);
        Point currentPt = new Point(0, 0);
        bool draged = false;
        bool bctrl = false;
        bool bshl = false;
        Pen p = new Pen(SystemColors.Highlight);
        private List<TreeNode> m_SelectedNodes = null;
        public bool MultiSelection
        {
            set
            {
                _MultiSelection = value;
            }
            get
            {
                return _MultiSelection;
            }
        }
        public List<TreeNode> SelectedNodes
        {
            get
            {
                return m_SelectedNodes;
            }
            set
            {
                ClearSelectedNodes();
                if (value != null)
                {
                    foreach (TreeNode node in value)
                    {
                        ToggleNode(node, true);
                    }
                }
            }
        }

        // Note we use the new keyword to Hide the native treeview's SelectedNode property.
        private TreeNode m_SelectedNode;
        public new TreeNode SelectedNode
        {
            get
            {
                if (_MultiSelection)
                {
                    return m_SelectedNode;
                }
                return base.SelectedNode;
            }
            set
            {
                if (_MultiSelection)
                {
                    ClearSelectedNodes();
                    if (value != null)
                    {
                        SelectNode(value, false);
                    }
                }
                else
                {
                    m_SelectedNode = value;
                    base.SelectedNode = value;
                }
            }
        }

        #endregion
        public TreeView()
        {
            m_SelectedNodes = new List<TreeNode>();
            CheckForIllegalCrossThreadCalls = false;
            base.SelectedNode = null;
            p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
        }
        public void ClearSelection()
        {
            ClearSelectedNodes();
            SelectSingleNode(m_SelectedNode);
        }

        #region Overridden Events
        protected override void OnGotFocus(EventArgs e)
        {
            // Make sure at least one node has a selection
            // this way we can tab to the ctrl and use the 
            // keyboard to select nodes
            try
            {
                if (_MultiSelection)
                {
                    if (m_SelectedNode == null && this.TopNode != null)
                    {
                        ToggleNode(this.TopNode, true);
                    }
                }
                base.OnGotFocus(e);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            // If the user clicks on a node that was not
            // previously selected, select it now.
            try
            {
                if (_MultiSelection)
                {

                    base.SelectedNode = null;

                    TreeNode node = this.GetNodeAt(e.Location);
                    if (node != null)
                    {
                        int leftBound = node.Bounds.X; // - 20; // Allow user to click on image
                        if ((this.ImageList != null) && (node.ImageIndex >= 0))
                            leftBound = this.ImageList.ImageSize.Width;
                        int rightBound = node.Bounds.Right + 10; // Give a little extra room
                        if (e.Location.X > leftBound && e.Location.X < rightBound)
                        {
                            if (ModifierKeys == Keys.None && (m_SelectedNodes.Contains(node)))
                            {
                                // Potential Drag Operation
                                // Let Mouse Up do select
                            }
                            else
                            {
                                SelectNode(node, e.Button == MouseButtons.Right);
                            }
                        }
                    }
                    currentPt.X = e.X;
                    currentPt.Y = e.Y;
                    OldPt.X = e.X;
                    OldPt.Y = e.Y;
                    if (e.Button == MouseButtons.Left)
                    {
                        draged = true;
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_MultiSelection)
            {

                if (draged && (e.Button == MouseButtons.Left))
                {
                    int maxX, maxY, minX, minY;
                    maxX = Math.Max(currentPt.X, OldPt.X);
                    maxY = Math.Max(currentPt.Y, OldPt.Y);
                    minX = Math.Min(currentPt.X, OldPt.X);
                    minY = Math.Min(currentPt.Y, OldPt.Y);
                    Graphics g = Graphics.FromHwnd(this.Handle);
                    Rectangle rc = new Rectangle(minX - 2, minY - 2, maxX - minX + 4, 4);
                    Invalidate(rc);
                    rc = new Rectangle(minX - 2, minY - 2, 4, maxY - minY + 4);
                    Invalidate(rc);
                    rc = new Rectangle(maxX - 2, minY - 2, 4, maxY - minY + 4);
                    Invalidate(rc);
                    rc = new Rectangle(minX - 2, maxY - 2, maxX - minX + 4, 4);
                    Invalidate(rc);

                    currentPt.X = e.X;
                    currentPt.Y = e.Y;
                    ValidateSelectedNodes(true);
                    maxX = Math.Max(currentPt.X, OldPt.X);
                    maxY = Math.Max(currentPt.Y, OldPt.Y);
                    minX = Math.Min(currentPt.X, OldPt.X);
                    minY = Math.Min(currentPt.Y, OldPt.Y);
                    DrawRectSelect(g);
                    g.Dispose();
                }
            }
            base.OnMouseMove(e);
        }
        private void ValidateSelectedNodes(bool removeExists)
        {
            int maxX, maxY, minX, minY, itemHeight;
            maxX = Math.Max(currentPt.X, OldPt.X);
            maxY = Math.Max(currentPt.Y, OldPt.Y);
            minX = Math.Min(currentPt.X, OldPt.X);
            minY = Math.Min(currentPt.Y, OldPt.Y);
            Rectangle r = new Rectangle(minX, minY, maxX - minX, maxY - minY);
            itemHeight = this.ItemHeight;
            System.Collections.ArrayList arr = new System.Collections.ArrayList();
            TreeNode nd = null;
            for (int j = minX; j <= maxX; j += 1)
            {
                nd = GetNodeAt(j, minY);
                if (nd != null) break;
            }
            if ((nd != null))
            {
                if (!bctrl) this.ClearSelectedNodes();
                if ((!arr.Contains(nd)) && (IntersectRect(r, nd.Bounds)))
                {
                    this.ToggleNode(nd, true);
                }
                for (int i = nd.Bounds.Bottom + 1; i <= maxY; i += itemHeight)
                {
                    TreeNode snd = null;
                    snd = GetNodeAt(minX, i);
                    if ((snd != null) && (!arr.Contains(snd)))
                    {
                        if ((IntersectRect(r, nd.Bounds)))
                        {
                            arr.Add(snd);
                            this.ToggleNode(snd, true);
                        }
                    }
                }
            }
        }
        private bool IntersectRect(Rectangle rc2, Rectangle rc1)
        {
            bool bresutl = PointInRect(rc1.Left, rc1.Top, rc2);
            bresutl = bresutl || (PointInRect(rc1.Left, rc1.Bottom, rc2));
            bresutl = bresutl || (PointInRect(rc1.Right, rc1.Top, rc2));
            bresutl = bresutl || (PointInRect(rc1.Right, rc1.Bottom, rc2));
            return bresutl;
        }
        private bool PointInRect(int X, int Y, Rectangle rc)
        {
            bool bresult = rc.Contains(X, Y);
            return bresult;
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            // If the clicked on a node that WAS previously
            // selected then, reselect it now. This will clear
            // any other selected nodes. e.g. A B C D are selected
            // the user clicks on B, now A C & D are no longer selected.

            try
            {
                if (_MultiSelection)
                {

                    // Check to see if a node was clicked on 
                    TreeNode node = this.GetNodeAt(e.Location);
                    if (node != null)
                    {
                        if (ModifierKeys == Keys.None && m_SelectedNodes.Contains(node))
                        {
                            int leftBound = node.Bounds.X; // -20; // Allow user to click on image
                            int rightBound = node.Bounds.Right + 10; // Give a little extra room
                            if (e.Location.X > leftBound && e.Location.X < rightBound)
                            {

                                SelectNode(node, e.Button == MouseButtons.Right);
                            }
                        }
                    }
                    int maxX = Math.Max(currentPt.X, OldPt.X);
                    int maxY = Math.Max(currentPt.Y, OldPt.Y);
                    int minX = Math.Min(currentPt.X, OldPt.X);
                    int minY = Math.Min(currentPt.Y, OldPt.Y);
                    Graphics g = Graphics.FromHwnd(this.Handle);
                    Rectangle rc = new Rectangle(minX - 2, minY - 2, maxX - minX + 4, 4);
                    Invalidate(rc);
                    rc = new Rectangle(minX - 2, minY - 2, 4, maxY - minY + 4);
                    Invalidate(rc);
                    rc = new Rectangle(maxX - 2, minY - 2, 4, maxY - minY + 4);
                    Invalidate(rc);
                    rc = new Rectangle(minX - 2, maxY - 2, maxX - minX + 4, 4);
                    Invalidate(rc);
                    g.Dispose();
                    this.OldPt.X = currentPt.X;
                    this.OldPt.Y = currentPt.Y;
                    draged = false;
                }

            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
            base.OnMouseUp(e);
        }

        protected override void OnItemDrag(ItemDragEventArgs e)
        {
            // If the user drags a node and the node being dragged is NOT
            // selected, then clear the active selection, select the
            // node being dragged and drag it. Otherwise if the node being
            // dragged is selected, drag the entire selection.
            try
            {
                if (_MultiSelection)
                {

                    TreeNode node = e.Item as TreeNode;

                    if (node != null)
                    {
                        if (!m_SelectedNodes.Contains(node))
                        {
                            SelectSingleNode(node);
                            ToggleNode(node, true);
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
            base.OnItemDrag(e);
        }

        protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
        {
            // Never allow base.SelectedNode to be set!
            try
            {
                if (_MultiSelection)
                {
                    base.SelectedNode = null;
                    e.Cancel = true;
                }

            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
            base.OnBeforeSelect(e);
        }

        protected override void OnAfterSelect(TreeViewEventArgs e)
        {
            // Never allow base.SelectedNode to be set!
            try
            {
                base.OnAfterSelect(e);
                if (_MultiSelection)
                {
                    base.SelectedNode = null;
                }
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            // Handle all possible key strokes for the control.
            // including navigation, selection, etc.

            base.OnKeyDown(e);
            if (_MultiSelection)
            {

                if (e.KeyCode == Keys.ShiftKey) return;

                //this.BeginUpdate();
                bool bShift = (ModifierKeys == Keys.Shift);
                bctrl = ModifierKeys == Keys.Control;
                try
                {
                    // Nothing is selected in the tree, this isn't a good state
                    // select the top node
                    if (m_SelectedNode == null && this.TopNode != null)
                    {
                        ToggleNode(this.TopNode, true);
                    }

                    // Nothing is still selected in the tree, this isn't a good state, leave.
                    if (m_SelectedNode == null) return;

                    if (e.KeyCode == Keys.Left)
                    {
                        if (m_SelectedNode.IsExpanded && m_SelectedNode.Nodes.Count > 0)
                        {
                            // Collapse an expanded node that has children
                            m_SelectedNode.Collapse();
                        }
                        else if (m_SelectedNode.Parent != null)
                        {
                            // Node is already collapsed, try to select its parent.
                            SelectSingleNode(m_SelectedNode.Parent);
                        }
                    }
                    else if (e.KeyCode == Keys.Right)
                    {
                        if (!m_SelectedNode.IsExpanded)
                        {
                            // Expand a collpased node's children
                            m_SelectedNode.Expand();
                        }
                        else
                        {
                            // Node was already expanded, select the first child
                            SelectSingleNode(m_SelectedNode.FirstNode);
                        }
                    }
                    else if (e.KeyCode == Keys.Up)
                    {
                        // Select the previous node
                        if (m_SelectedNode.PrevVisibleNode != null)
                        {
                            SelectNode(m_SelectedNode.PrevVisibleNode, false);
                        }
                    }
                    else if (e.KeyCode == Keys.Down)
                    {
                        // Select the next node
                        if (m_SelectedNode.NextVisibleNode != null)
                        {
                            SelectNode(m_SelectedNode.NextVisibleNode, false);
                        }
                    }
                    else if (e.KeyCode == Keys.Home)
                    {
                        if (bShift)
                        {
                            if (m_SelectedNode.Parent == null)
                            {
                                // Select all of the root nodes up to this point 
                                if (this.Nodes.Count > 0)
                                {
                                    SelectNode(this.Nodes[0], false);
                                }
                            }
                            else
                            {
                                // Select all of the nodes up to this point under this nodes parent
                                SelectNode(m_SelectedNode.Parent.FirstNode, false);
                            }
                        }
                        else
                        {
                            // Select this first node in the tree
                            if (this.Nodes.Count > 0)
                            {
                                SelectSingleNode(this.Nodes[0]);
                            }
                        }
                    }
                    else if (e.KeyCode == Keys.End)
                    {
                        if (bShift)
                        {
                            if (m_SelectedNode.Parent == null)
                            {
                                // Select the last ROOT node in the tree
                                if (this.Nodes.Count > 0)
                                {
                                    SelectNode(this.Nodes[this.Nodes.Count - 1], false);
                                }
                            }
                            else
                            {
                                // Select the last node in this branch
                                SelectNode(m_SelectedNode.Parent.LastNode, false);
                            }
                        }
                        else
                        {
                            if (this.Nodes.Count > 0)
                            {
                                // Select the last node visible node in the tree.
                                // Don't expand branches incase the tree is virtual
                                TreeNode ndLast = this.Nodes[0].LastNode;
                                while (ndLast.IsExpanded && (ndLast.LastNode != null))
                                {
                                    ndLast = ndLast.LastNode;
                                }
                                SelectSingleNode(ndLast);
                            }
                        }
                    }
                    else if (e.KeyCode == Keys.PageUp)
                    {
                        // Select the highest node in the display
                        int nCount = this.VisibleCount;
                        TreeNode ndCurrent = m_SelectedNode;
                        while ((nCount) > 0 && (ndCurrent.PrevVisibleNode != null))
                        {
                            ndCurrent = ndCurrent.PrevVisibleNode;
                            nCount--;
                        }
                        SelectSingleNode(ndCurrent);
                    }
                    else if (e.KeyCode == Keys.PageDown)
                    {
                        // Select the lowest node in the display
                        int nCount = this.VisibleCount;
                        TreeNode ndCurrent = m_SelectedNode;
                        while ((nCount) > 0 && (ndCurrent.NextVisibleNode != null))
                        {
                            ndCurrent = ndCurrent.NextVisibleNode;
                            nCount--;
                        }
                        SelectSingleNode(ndCurrent);
                    }
                    else
                    {
                        // Assume this is a search character a-z, A-Z, 0-9, etc.
                        // Select the first node after the current node that 
                        // starts with this character
                        string sSearch = ((char)e.KeyValue).ToString();

                        TreeNode ndCurrent = m_SelectedNode;
                        while ((ndCurrent.NextVisibleNode != null))
                        {
                            ndCurrent = ndCurrent.NextVisibleNode;
                            if (ndCurrent.Text.StartsWith(sSearch))
                            {
                                SelectSingleNode(ndCurrent);
                                break;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
                finally
                {
                    this.EndUpdate();
                }
            }
        }
        private void DrawRectSelect(Graphics g)
        {
            int maxX, maxY, minX, minY;
            maxX = Math.Max(currentPt.X, OldPt.X);
            maxY = Math.Max(currentPt.Y, OldPt.Y);
            minX = Math.Min(currentPt.X, OldPt.X);
            minY = Math.Min(currentPt.Y, OldPt.Y);
            g.DrawRectangle(p, minX, minY, maxX - minX, maxY - minY);

        }
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            switch (m.Msg)
            {
                case Commons.Win32.WM_PAINT:
                    if ((this.draged) && (!bshl) && (_MultiSelection))
                    {
                        Graphics g = this.CreateGraphics();
                        DrawRectSelect(g);
                        g.Dispose();
                    }
                    break;

            }
        }
        #endregion
        #region Helper Methods

        private void SelectNode(TreeNode node, bool RightClick)
        {
            try
            {
                this.BeginUpdate();

                if (m_SelectedNode == null || ModifierKeys == Keys.Control)
                {
                    // Ctrl+Click selects an unselected node, or unselects a selected node.
                    bool bIsSelected = m_SelectedNodes.Contains(node);
                    ToggleNode(node, !bIsSelected);
                }
                else if (ModifierKeys == Keys.Shift)
                {
                    // Shift+Click selects nodes between the selected node and here.
                    TreeNode ndStart = m_SelectedNode;
                    TreeNode ndEnd = node;

                    if (ndStart.Parent == ndEnd.Parent)
                    {
                        // Selected node and clicked node have same parent, easy case.
                        if (ndStart.Index < ndEnd.Index)
                        {
                            // If the selected node is beneath the clicked node walk down
                            // selecting each Visible node until we reach the end.
                            while (ndStart != ndEnd)
                            {
                                ndStart = ndStart.NextVisibleNode;
                                if (ndStart == null) break;
                                ToggleNode(ndStart, true);
                            }
                        }
                        else if (ndStart.Index == ndEnd.Index)
                        {
                            // Clicked same node, do nothing
                        }
                        else
                        {
                            // If the selected node is above the clicked node walk up
                            // selecting each Visible node until we reach the end.
                            while (ndStart != ndEnd)
                            {
                                ndStart = ndStart.PrevVisibleNode;
                                if (ndStart == null) break;
                                ToggleNode(ndStart, true);
                            }
                        }
                    }
                    else
                    {
                        // Selected node and clicked node have same parent, hard case.
                        // We need to find a common parent to determine if we need
                        // to walk down selecting, or walk up selecting.

                        TreeNode ndStartP = ndStart;
                        TreeNode ndEndP = ndEnd;
                        int startDepth = Math.Min(ndStartP.Level, ndEndP.Level);

                        // Bring lower node up to common depth
                        while (ndStartP.Level > startDepth)
                        {
                            ndStartP = ndStartP.Parent;
                        }

                        // Bring lower node up to common depth
                        while (ndEndP.Level > startDepth)
                        {
                            ndEndP = ndEndP.Parent;
                        }

                        // Walk up the tree until we find the common parent
                        while (ndStartP.Parent != ndEndP.Parent)
                        {
                            ndStartP = ndStartP.Parent;
                            ndEndP = ndEndP.Parent;
                        }

                        // Select the node
                        if (ndStartP.Index < ndEndP.Index)
                        {
                            // If the selected node is beneath the clicked node walk down
                            // selecting each Visible node until we reach the end.
                            while (ndStart != ndEnd)
                            {
                                ndStart = ndStart.NextVisibleNode;
                                if (ndStart == null) break;
                                ToggleNode(ndStart, true);
                            }
                        }
                        else if (ndStartP.Index == ndEndP.Index)
                        {
                            if (ndStart.Level < ndEnd.Level)
                            {
                                while (ndStart != ndEnd)
                                {
                                    ndStart = ndStart.NextVisibleNode;
                                    if (ndStart == null) break;
                                    ToggleNode(ndStart, true);
                                }
                            }
                            else
                            {
                                while (ndStart != ndEnd)
                                {
                                    ndStart = ndStart.PrevVisibleNode;
                                    if (ndStart == null) break;
                                    ToggleNode(ndStart, true);
                                }
                            }
                        }
                        else
                        {
                            // If the selected node is above the clicked node walk up
                            // selecting each Visible node until we reach the end.
                            while (ndStart != ndEnd)
                            {
                                ndStart = ndStart.PrevVisibleNode;
                                if (ndStart == null) break;
                                ToggleNode(ndStart, true);
                            }
                        }
                    }
                }
                else if (RightClick)
                {
                    bool bIsSelected = m_SelectedNodes.Contains(node);
                    if (bIsSelected)
                    {
                        ToggleNode(node, bIsSelected);
                    }
                    else
                    {
                        // Just clicked a node, select it
                        SelectSingleNode(node);
                    }
                }
                else
                {
                    // Just clicked a node, select it
                    SelectSingleNode(node);
                }
                OnAfterSelect(new TreeViewEventArgs(m_SelectedNode));
            }
            finally
            {
                this.EndUpdate();
            }
        }

        private void ClearSelectedNodes()
        {
            try
            {
                foreach (TreeNode node in m_SelectedNodes)
                {
                    node.BackColor = this.BackColor;
                    node.ForeColor = this.ForeColor;
                }
            }
            finally
            {
                m_SelectedNodes.Clear();
                m_SelectedNode = null;
            }
        }

        private void SelectSingleNode(TreeNode node)
        {
            if (node == null)
            {
                return;
            }
            ClearSelectedNodes();
            ToggleNode(node, true);
            node.EnsureVisible();
        }

        private void ToggleNode(TreeNode node, bool bSelectNode)
        {
            if (bSelectNode)
            {
                m_SelectedNode = node;
                if (!m_SelectedNodes.Contains(node))
                {
                    m_SelectedNodes.Add(node);
                }
                node.BackColor = SystemColors.Highlight;
                node.ForeColor = SystemColors.HighlightText;
            }
            else
            {
                m_SelectedNodes.Remove(node);
                node.BackColor = this.BackColor;
                node.ForeColor = this.ForeColor;
            }
        }

        private void HandleException(Exception ex)
        {
            // Perform some error handling here.
            // We don't want to bubble errors to the CLR. 
            MessageBox.Show(ex.Message);
        }

        #endregion
    }
}
